// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_PUBLIC_TEST_TEST_FILE_ERROR_INJECTOR_H_
#define CONTENT_PUBLIC_TEST_TEST_FILE_ERROR_INJECTOR_H_

#include <stddef.h>

#include <memory>
#include <string>

#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "content/public/browser/download_interrupt_reasons.h"
#include "url/gurl.h"

namespace content {

class DownloadFileWithErrorFactory;
class DownloadManager;
class DownloadManagerImpl;

// Test helper for injecting errors into download file operations.  All errors
// for a download must be injected before it starts.  This class needs to be
// |RefCountedThreadSafe| because the implementation is referenced by other
// classes that live past the time when the user is nominally done with it.
//
// Once created, an error injected via InjectError() will cause any
// DownloadFiles created to fail with that error. Call ClearError() to stop
// injecting errors.
//
// Example:
//
// FileErrorInfo a = { ... };
//
// scoped_refptr<TestFileErrorInjector> injector =
//     TestFileErrorInjector::Create(download_manager);
//
// injector->InjectError(a);
//
// download_manager->DownloadUrl(url1, ...); // Will be interrupted due to |a|.
// download_manager->DownloadUrl(url2, ...); // Will be interrupted due to |a|.
//
// injector->ClearError();
//
// download_manager->DownloadUrl(url3, ...); // Won't be interrupted due to |a|.
class TestFileErrorInjector
    : public base::RefCountedThreadSafe<TestFileErrorInjector> {
public:
    enum FileOperationCode {
        FILE_OPERATION_INITIALIZE,
        FILE_OPERATION_WRITE,
        FILE_OPERATION_RENAME_UNIQUIFY,
        FILE_OPERATION_RENAME_ANNOTATE,
    };

    // Structure that encapsulates the information needed to inject a file error.
    struct FileErrorInfo {
        FileOperationCode code; // Operation to affect.
        int operation_instance; // 0-based count of operation calls, for each code.
        DownloadInterruptReason error; // Error to inject.
    };

    // Creates an instance.  May only be called once.
    // Lives until all callbacks (in the implementation) are complete and the
    // creator goes out of scope.
    // TODO(rdsmith): Allow multiple calls for different download managers.
    static scoped_refptr<TestFileErrorInjector> Create(
        DownloadManager* download_manager);

    // Injects the errors such that new download files will be affected.
    // The download system must already be initialized before calling this.
    // Multiple calls are allowed, but only useful if the errors have changed.
    // Replaces the injected error list.
    bool InjectError(const FileErrorInfo& error_to_inject);

    // Clears all errors.
    // Only affects files created after the next call to InjectErrors().
    void ClearError();

    // Tells how many files are currently open.
    size_t CurrentFileCount() const;

    // Tells how many files have ever been open (since construction or the
    // last call to |ClearTotalFileCount()|).
    size_t TotalFileCount() const;

    // Resets the total file count. Doesn't affect what's returned by
    // CurrentFileCount().
    void ClearTotalFileCount();

    static std::string DebugString(FileOperationCode code);

private:
    friend class base::RefCountedThreadSafe<TestFileErrorInjector>;

    explicit TestFileErrorInjector(DownloadManager* download_manager);

    virtual ~TestFileErrorInjector();

    // Callbacks from the download file, to record lifetimes.
    // These may be called on any thread.
    void RecordDownloadFileConstruction();
    void RecordDownloadFileDestruction();

    // These run on the UI thread.
    void DownloadFileCreated();
    void DestroyingDownloadFile();

    // All the data is used on the UI thread.
    // Keep track of active DownloadFiles.
    size_t active_file_count_ = 0;

    // Keep track of found DownloadFiles.
    size_t total_file_count_ = 0;

    // The factory we created. May outlive this class.
    DownloadFileWithErrorFactory* created_factory_ = nullptr;

    // The download manager we set the factory on.
    DownloadManagerImpl* download_manager_ = nullptr;

    DISALLOW_COPY_AND_ASSIGN(TestFileErrorInjector);
};

} // namespace content

#endif // CONTENT_PUBLIC_TEST_TEST_FILE_ERROR_INJECTOR_H_
